package enricher

import (
	"context"

	"github.com/pkg/errors"
	"github.com/stackrox/rox/generated/storage"
	"github.com/stackrox/rox/pkg/cvss"
	"github.com/stackrox/rox/pkg/errox"
	"github.com/stackrox/rox/pkg/features"
	"github.com/stackrox/rox/pkg/images"
	"github.com/stackrox/rox/pkg/images/types"
	"github.com/stackrox/rox/pkg/images/utils"
	"github.com/stackrox/rox/pkg/openshift"
	registryTypes "github.com/stackrox/rox/pkg/registries/types"
	"golang.org/x/sync/semaphore"
)

// EnrichImageByName takes an image name, and returns the corresponding enriched image.
func EnrichImageByName(ctx context.Context, enricher ImageEnricher, enrichmentCtx EnrichmentContext, name string) (*storage.Image, error) {
	if name == "" {
		return nil, errors.Wrap(errox.InvalidArgs, "image name must be specified")
	}
	containerImage, err := utils.GenerateImageFromString(name)
	if err != nil {
		return nil, errors.Wrap(errox.InvalidArgs, err.Error())
	}
	img := types.ToImage(containerImage)

	enrichmentResult, err := enricher.EnrichImage(ctx, enrichmentCtx, img)
	if err != nil {
		return nil, err
	}

	if !enrichmentResult.ImageUpdated || (enrichmentResult.ScanResult != ScanSucceeded) {
		return nil, errors.Errorf("scan could not be completed for image %s (%v): please check that an applicable registry and scanner is integrated", name, img.GetNotes())
	}

	return img, nil
}

// EnrichImageV2ByName takes an image name, and returns the corresponding enriched image.
func EnrichImageV2ByName(ctx context.Context, enricher ImageEnricherV2, enrichmentCtx EnrichmentContext, name string) (*storage.ImageV2, error) {
	if name == "" {
		return nil, errors.Wrap(errox.InvalidArgs, "image name must be specified")
	}
	containerImage, err := utils.GenerateImageFromString(name)
	if err != nil {
		return nil, errors.Wrap(errox.InvalidArgs, err.Error())
	}
	img := types.ToImageV2(containerImage)

	enrichmentResult, err := enricher.EnrichImage(ctx, enrichmentCtx, img)
	if err != nil {
		return nil, err
	}

	if !enrichmentResult.ImageUpdated || (enrichmentResult.ScanResult != ScanSucceeded) {
		return nil, errors.Errorf("scan could not be completed for image %s (%v): please check that an applicable registry and scanner is integrated", name, img.GetNotes())
	}

	return img, nil
}

func imageIntegrationToDataSource(i *storage.ImageIntegration) *storage.DataSource {
	return &storage.DataSource{
		Id:   i.GetId(),
		Name: i.GetName(),
	}
}

func normalizeVulnerabilities(scan *storage.ImageScan) {
	for _, c := range scan.GetComponents() {
		for _, v := range c.GetVulns() {
			v.Severity = cvss.VulnToSeverity(cvss.NewFromEmbeddedVulnerability(v))
		}
	}
}

func acquireSemaphoreWithMetrics(semaphore *semaphore.Weighted, ctx context.Context) error {
	images.ScanSemaphoreQueueSize.WithLabelValues("central", "central-image-enricher", "n/a").Inc()
	defer images.ScanSemaphoreQueueSize.WithLabelValues("central", "central-image-enricher", "n/a").Dec()
	err := semaphore.Acquire(ctx, 1)
	if err != nil {
		return err
	}
	images.ScanSemaphoreHoldingSize.WithLabelValues("central", "central-image-enricher", "n/a").Inc()
	return nil
}

func registryNames(registries []registryTypes.ImageRegistry) []string {
	names := make([]string, 0, len(registries))
	for _, reg := range registries {
		names = append(names, reg.Name())
	}
	return names
}

// filterRegistriesBySource will filter the registries based on the following conditions:
// 1. If the registry is autogenerated
// 2. If the integration's source matches with the EnrichmentContext.Source
// Note that this function WILL modify the input array.
func filterRegistriesBySource(requestSource *RequestSource, registries []registryTypes.ImageRegistry) {
	if !features.SourcedAutogeneratedIntegrations.Enabled() {
		return
	}

	filteredRegistries := registries[:0]
	for _, registry := range registries {
		integration := registry.Source()
		if !integration.GetAutogenerated() {
			filteredRegistries = append(filteredRegistries, registry)
			continue
		}
		source := integration.GetSource()
		if source.GetClusterId() != requestSource.ClusterID {
			continue
		}
		// Check if the integration source is the global OpenShift registry
		if openshift.GlobalPullSecretIntegration(integration) {
			filteredRegistries = append(filteredRegistries, registry)
			continue
		}
		if source.GetNamespace() != requestSource.Namespace {
			continue
		}
		if !requestSource.ImagePullSecrets.Contains(source.GetImagePullSecretName()) {
			continue
		}
		filteredRegistries = append(filteredRegistries, registry)
	}
}
